import {
    CSSProperties,
    FC,
    ReactNode,
    useRef,
    useEffect,
    useState
} from 'react';

// className 和 style 是给外层 div 添加样式的。

// placeholder 是占位的内容。

// offset 是距离到可视区域多远就触发加载。

// onContentVisible 是进入可视区域的回调。

// 然后用 useRef 保存外层 div 的引用。

// 用 useState 保存 visible 状态。

// visible 的时候展示 children，否则展示 placeholder。

interface MyLazyloadProps{
    className?: string,
    style?: CSSProperties,
    placeholder?: ReactNode,
    offset?: string | number,
    width?: number | string,
    height?: string | number,
    onContentVisible?: () => void,
    children: ReactNode,
}
// children 是说父组件里面 MyLazyload 必须包含子元素.否则也没有使用MyLazyload组件的必要了
const MyLazyload: FC<MyLazyloadProps> = (props) => {

    const {
        className = '',
        style,
        offset = 0,
        width,
        onContentVisible,
        placeholder,
        height,
        children
    } = props;
// containerRef 指向的div ,用于获取本组件里面的div. 当 div 元素被渲染到 DOM 中时，containerRef.current 就会指向这个 div 元素
    const containerRef = useRef<HTMLDivElement>(null);
    // 是否显示内容,children
    const [visible, setVisible] = useState(false);
    // IntersectionObserver 用于异步观察目标元素与其祖先元素或顶级文档视窗（viewport）交叉状态
    const elementObserver = useRef<IntersectionObserver>();
    // lazyLoadHandler 是 IntersectionObserver 的回调函数，当目标元素进入或离开可视区域时会被调用
    function lazyLoadHandler (entries: IntersectionObserverEntry[]) {
        // IntersectionObserverEntry 包含以下属性：
        // boundingClientRect: 目标元素的边界矩形，返回一个 DOMRectReadOnly 对象。
        // intersectionRect: 目标元素与根元素交叉的矩形区域，返回一个 DOMRectReadOnly 对象。
        // intersectionRatio: 目标元素可见区域占其整个区域的比例，值在 0 到 1 之间。
        // isIntersecting: 一个布尔值，表示目标元素是否与根元素交叉。
        // rootBounds: 根元素的边界矩形，返回一个 DOMRectReadOnly 对象。
        // target: 被观察的目标元素，返回一个 Element 对象。
        // time: 交叉状态发生变化的时间戳，相对于 IntersectionObserver 的时间原点。
        // IntersectionObserverEntry 包含了目标元素与根元素（root）的交叉状态信息
        const [entry] = entries;
        const { isIntersecting } = entry;
        // isIntersecting: 表示目标元素是否与根元素交叉
        if (isIntersecting) {
            // 如果交叉，则设置显示为true
            setVisible(true);
            // 执行传入的函数
            onContentVisible?.();
            // unobserve 取消对元素的观察
            const node = containerRef.current;
            if (node && node instanceof HTMLElement) {
                elementObserver.current?.unobserve(node);
            }
        }
    };
    useEffect(() => {
        // 此代码段用于配置Intersection
        // Observer的选项，并创建一个新的Intersection
        // Observer实例。Intersection
        // Observer是一个浏览器API，用于异步观察目标元素与视口（或指定元素）的交叉情况，常用于实现图片懒加载、无限滚动等效果
        const options = {
            rootMargin: typeof offset === 'number' ? `${offset}px` : offset || '0px',
            threshold: 0
        };
        // lazyLoadHandler 是一个回调函数，当目标元素与视口（或指定元素）的交叉情况发生变化时，这个函数会被调用
        elementObserver.current = new IntersectionObserver(lazyLoadHandler, options);
        // 观察node 对象
        const node = containerRef.current;
        if (node instanceof HTMLElement) {
            elementObserver.current.observe(node);
        }

        // 这段代码确保在组件卸载时，或者当依赖数组发生变化导致新的副作用函数执行之前，取消对node元素的观察。这样可以避免内存泄漏和不必要的观察操作，确保资源的正确释放。
        // 总结一下，返回清理函数的主要目的是在适当的时机（如组件卸载或依赖变化）清理副作用，以确保不会留下未处理的观察器或其他资源占用。

        // 组件在卸载时或者依赖变化导致新的副作用函数执行之前,取消对node元素的观察
        return () => {
            if (node && node instanceof HTMLElement) {
                elementObserver.current?.unobserve(node);
            }
        }
    }, []);
    const styles = { height, width, ...style };

    return <div ref={containerRef} className={className} style={styles}>
        {visible? children : placeholder}
    </div>
}

export default MyLazyload;